package framework;


import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import framework.util._BeanUtil;
import framework.util.jdkExt._FileUtil;
import framework.util.generator.FieldForGeneratorMysql;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.sql.*;
import java.util.*;

/**
 * 代码生成器
 *
 * @author 吴宇亮 on 2019/06/27
 */
public class Generator {

    //要生成javabean的数据库表名，如：sys_code
    private static final String tableName = "sys_user_role";

    //要将javabean生成到下面指定的目录，如：D:\idea workspace\framework_web_gradle\code\framework_web\src\main\java\pratice\generatorTest\bo
    private static final String javaBeanPath = "E:\\codingFold\\ideaWorkspace\\framework_web_gradle\\code\\framework_web\\src\\main\\java\\framework\\bo";

    @Test
    public void generator() throws Exception{
        generatorJavaBean(true);
        generatorController();
        generatorService();
        generatorServiceImpl();
        generatorDao();
        generatorDaoXml();

        System.out.println(tableName + "的代码已经生成完毕");
    }

    private void generatorController() throws Exception {
        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\controller";
        _FileUtil.createDir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + javabeanName + "Controller.java");
        if(file.exists()){
            System.out.println("controller已经存在，不进行生成，以免覆盖");
            System.out.println("------------------------------");
            return;
        }

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath + "/java/controller"));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("controller.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        generatorMap.put("package", "package " + basicPackageName + ".controller;");
        //导入service
        generatorMap.put("importServicePath", basicPackageName + ".service." + javabeanName +"Service;");
        //拦截路径
        generatorMap.put("requestMapping", "/" + basicPackageName.replaceAll("/.", "//") + "/" + javabeanName + ".do");
        //controller类名
        generatorMap.put("controllerName", javabeanName + "Controller");
        //service名
        generatorMap.put("serviceName", javabeanName + "Service");
        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(tableName + "的controller已经生成完毕");
        System.out.println("------------------------------");
    }

    /**
     * @param isForceGenerator 是否强制生成，强制生成则有可能覆盖文件
     */
    private void generatorJavaBean(boolean isForceGenerator) throws Exception {
        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\bo";
        _FileUtil.createDir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + javabeanName + ".java");
        if(!isForceGenerator){
            if(file.exists()){
                System.out.println("javaBean已经存在，不进行生成，以免覆盖");
                System.out.println("------------------------------");
                return;
            }
        }

        //读取数据源，获得表的字段信息List<FieldForGeneratorMysql>
        List<FieldForGeneratorMysql> fieldList = getColumnList();

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath + "/java/bo"));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("javabean.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        generatorMap.put("package", "package " + basicPackageName + ".bo;");
        //类名
        generatorMap.put("javabeanName", javabeanName);
        //字段名
        generatorMap.put("fieldForGeneratorList", fieldList);
        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(tableName + "的javabean已经生成完毕");
        System.out.println("------------------------------");
    }

    public void generatorService() throws Exception {
        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\service";
        _FileUtil.createDir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + javabeanName + "Service" + ".java");
        if(file.exists()){
            System.out.println("service已经存在，不进行生成，以免覆盖");
            System.out.println("------------------------------");
            return;
        }

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath + "/java/service"));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("service.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        generatorMap.put("package", "package " + basicPackageName + ".service;");
        generatorMap.put("importList", Arrays.asList("import " + basicPackageName + ".bo." + javabeanName + ";", "import framework.util.queryFilter.service.IBaseService;"));
        generatorMap.put("javabeanName", javabeanName);
        generatorMap.put("interfaceName", javabeanName + "Service");

        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(tableName + "的Service已经生成完毕");
        System.out.println("------------------------------");
    }

    private void generatorServiceImpl() throws Exception {
        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\service\\impl";
        _FileUtil.createDir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + javabeanName + "ServiceImpl" + ".java");
        if(file.exists()){
            System.out.println("serviceImpl已经存在，不进行生成，以免覆盖");
            System.out.println("------------------------------");
            return;
        }

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath + "/java/service/impl"));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("serviceImpl.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        generatorMap.put("package", "package " + basicPackageName + ".service.impl;");
        generatorMap.put("importList", Lists.newArrayList(
                "import " + basicPackageName + ".bo." + javabeanName + ";"
                , "import " + basicPackageName + ".dao.I" + javabeanName + "Dao;"
                , "import " + basicPackageName + ".service." + javabeanName + "Service;"
        ));
        generatorMap.put("javabeanName", javabeanName);
        generatorMap.put("className", javabeanName + "ServiceImpl");
        generatorMap.put("interfaceName", javabeanName + "Service");
        generatorMap.put("daoClassName", "I" + javabeanName + "Dao");

        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(javabeanName + "的ServiceImpl已经生成完毕");
        System.out.println("------------------------------");
    }

    public void generatorDao() throws Exception {
        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\dao";
        _FileUtil.createDir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + "I" + javabeanName + "Dao" + ".java");
        if(file.exists()){
            System.out.println("dao已经存在，不进行生成，以免覆盖");
            System.out.println("------------------------------");
            return;
        }

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath + "/java/dao"));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("dao.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        generatorMap.put("package", "package " + basicPackageName + ".dao;");
        generatorMap.put("interfaceName", "I" + javabeanName + "Dao");

        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(javabeanName + "的dao已经生成完毕");
        System.out.println("------------------------------");
    }

    private void generatorDaoXml()throws Exception {
        //如果文件已经存在，则不进行生成。
        String generatorPath = basicGeneratorPath + "\\dao";
        _FileUtil.createDir(new File(generatorPath));
        File file = new File(generatorPath + "\\" + "I" + javabeanName + "Dao" + ".xml");
        if(file.exists()){
            System.out.println("daoXml已经存在，不进行生成，以免覆盖");
            System.out.println("------------------------------");
            return;
        }

        // 第一步：实例化Freemarker的配置类, 配置ftl文件所在的路径
        Configuration conf = new Configuration();
        conf.setDirectoryForTemplateLoading(new File(templateFoldPath + "/java/dao"));

        // 第二步：设置ftl模板的路径 、初始化数据源、最后文件要输出的位置
        Template template = conf.getTemplate("daoXml.ftl");						//设置ftl模板的路径
        Map<String, Object> generatorMap = new HashMap<>(); 		//定义数据源
        Writer out = new FileWriter(file); //定义最终文件生成位置

        // 第三步：往数据源里添加数据，将数据嵌入模板中，生成文件
        generatorMap.put("interfaceQualifiedName", basicPackageName + ".dao.I" + javabeanName + "Dao");

        // 执行生成
        template.process(generatorMap, out);
        out.flush();
        out.close();

        System.out.println(javabeanName + "的daoXml已经生成完毕");
        System.out.println("------------------------------");
    }

    /**
     * 读取数据源，获得表的字段信息List<FieldForGeneratorMysql>
     * @return 返回tableName的字段信息
     */
    private List<FieldForGeneratorMysql> getColumnList() throws SQLException {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/spring-db.xml");
        ComboPooledDataSource dataSource = (ComboPooledDataSource)applicationContext.getBean("dataSource");
        Connection connection = dataSource.getConnection();

        //查询表中有什么字段
        PreparedStatement preparedStatement = connection.prepareStatement("desc " + tableName);
        ResultSet resultSet = preparedStatement.executeQuery();

        List<FieldForGeneratorMysql> list = new ArrayList<>();
        ResultSetMetaData row = resultSet.getMetaData();//获取元数据
        int columnCount = row.getColumnCount();//获取列的数量
        while (resultSet.next()) {
            Map rowData = new HashMap();//声明Map
            for (int i = 1; i <= columnCount; i++) {
                rowData.put(row.getColumnName(i), resultSet.getObject(i));//获取键名及值
            }
            FieldForGeneratorMysql columnVo = JSON.parseObject(JSON.toJSONString(rowData), FieldForGeneratorMysql.class);
            list.add(columnVo);
        }
        preparedStatement.close();
        connection.close();

        for (FieldForGeneratorMysql column : list) {
            column.setColumnRealName(_BeanUtil.transform_2UpperCase(column.getColumnName()));
            column.setPropName(column.getColumnName().toUpperCase());
            if(column.getColumnType().startsWith(FieldForGeneratorMysql.VARCHAR) || column.getColumnType().startsWith(FieldForGeneratorMysql.CHAR)){
                column.setColumnRealType("String");
            }else if(column.getColumnType().startsWith(FieldForGeneratorMysql.INT)){
                column.setColumnRealType("Integer");
            }else if(column.getColumnType().startsWith(FieldForGeneratorMysql.DECIMAL)){
                column.setColumnRealType("Double");
            }else if(column.getColumnType().startsWith(FieldForGeneratorMysql.DATE)){
                column.setColumnRealType("Date");
            }

            column.setGetterMethodName(_BeanUtil.getGetterMethodName(column.getColumnRealName()));
            column.setSetterMethodName(_BeanUtil.getSetterMethodName(column.getColumnRealName()));
        }
        return list;
    }

    /** 存放flt生成模板文件的目录路径 **/
    private static final String templateFoldPath;
    private static final String basicGeneratorPath;
    private static final String basicPackageName;
    private static final String javabeanName;

    static {
        templateFoldPath = javaBeanPath.substring(0, javaBeanPath.indexOf("\\src\\main") + "\\src\\main".length()) + "\\resources\\generatorTemplate";

        //根据javaBean获取要生成代码的基本的路径basicGeneratorPath
        //如：D:\idea workspace\framework_web_gradle\code\framework_web\src\main\java\framework\bo 获取 D:\idea workspace\framework_web_gradle\code\framework_web\src\main\java\framework
        basicGeneratorPath = javaBeanPath.substring(0, javaBeanPath.lastIndexOf("\\"));

        //根据javaBean所在的路径获取基本的包名
        int startIndex = javaBeanPath.indexOf("src\\main\\java\\");
        int lastIndexOf = javaBeanPath.lastIndexOf("\\");
        if(-1 == startIndex){
            throw new IllegalArgumentException("javabean 只能生成在src/main/java目录下！");
        }
        basicPackageName = javaBeanPath.substring(startIndex + "src\\main\\java\\".length(), lastIndexOf).replaceAll("\\\\", ".");

        //获取javabean的类名
        String _javabeanName = _BeanUtil.transform_2UpperCase(tableName);
        javabeanName = _javabeanName.substring(0, 1).toUpperCase() + _javabeanName.substring(1);
    }


}
